package com.yboot.starter.common.core.utils;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.*;

public class TreeUtils<T> {
    private String idField = "id";
    private String parentIdField = "parentId";
    private String childrenField = "children";

    public TreeUtils() {
    }

    public TreeUtils(String idField, String parentIdField, String childrenField) {
        this.idField = idField;
        this.parentIdField = parentIdField;
        this.childrenField = childrenField;
    }

    /**
     * 将平面列表转换为树形结构
     * @param list 数据列表
     * @param rootId 根节点ID值
     */
    public List<T> buildTree(List<T> list, Object rootId) {
        Map<Object, T> nodeMap = new HashMap<>();
        for (T item : list) {
            nodeMap.put(this.getFieldValue(item, idField), item);
        }
        List<T> trees = new ArrayList<>();
        list.forEach(node -> {
            Object parentId = getFieldValue(node, parentIdField);
            if (rootId.equals(parentId)) {
                trees.add(node);
            } else {
                T parent = nodeMap.get(parentId);
                if (parent != null) {
                    List<T> children = getOrCreateChildren(parent);
                    children.add(node);
                }
            }
        });
        return trees;
    }

    @SuppressWarnings("unchecked")
    private List<T> getOrCreateChildren(T node) {
        try {
            Field field = node.getClass().getDeclaredField(childrenField);
            field.setAccessible(true);
            List<T> children = (List<T>) field.get(node);
            if (children == null) {
                children = new ArrayList<>();
                field.set(node, children);
            }
            return children;
        } catch (Exception e) {
            throw new RuntimeException("Failed to access children field", e);
        }
    }

    private Object getFieldValue(T node, String fieldName) {
        try {
            Field field = node.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(node);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get field value", e);
        }
    }

    /**
     * 测试用例
     */
    public static void main(String[] args) {
        List<Menu> menus = Arrays.asList(
            new Menu(1, 0, "系统管理"),
            new Menu(2, 1, "用户管理"),
            new Menu(5, 2, "用户管理-新增"),
            new Menu(3, 1, "角色管理"),
            new Menu(4, 0, "业务管理")
        );

        TreeUtils<Menu> utils = new TreeUtils<>();
        List<Menu> tree = utils.buildTree(menus, 0);
        System.out.println(JSONObject.toJSONString(tree));

        TreeNodeConfig config = new TreeNodeConfig()
                .setIdKey("id")
                .setParentIdKey("parentId");
        List<Tree<String>> tree2 = TreeUtil.build(menus, "0", config, (dto, node) -> {
            node.setId(dto.getId().toString());
            node.setParentId(dto.getParentId().toString());
            node.setName(dto.getName());
        });
        System.out.println(JSONObject.toJSONString(tree2));
    }

    @Data
    static class Menu {
        private Integer id;
        private Integer parentId;
        private String name;
        private List<Menu> children;

        public Menu(Integer id, Integer parentId, String name) {
            this.id = id;
            this.parentId = parentId;
            this.name = name;
        }
    }
}
